
#include "pywinpcapmodule.h"

extern PyTypeObject pywinpcap_device_type;
extern PyTypeObject pywinpcap_capture_type;
extern PyTypeObject pywinpcap_packet_type;

PyObject * PyExc_PyWinPCapError = NULL;
PyObject * PyExc_PyWinPCapTimeoutError = NULL;

static PyObject *
py_get_winpcap_version(PyObject *self, PyObject *args)
{
	Py_INCREF(Py_None);
	return Py_None;
}

PyDoc_STRVAR(get_winpcap_version_doc,
"get_winpcap_version() -> return WinPCap version");

static PyObject *
py_find_all_devices(PyObject * self, PyObject * args)
{
	char err_buff[PCAP_ERRBUF_SIZE];
	pcap_if_t * pcap_list = NULL;
	pcap_if_t * pcap_list_iter = NULL;
	PyObject * py_dev_tuple = NULL;
	Py_ssize_t py_dev_tuple_size = 0;
	Py_ssize_t i = 0;
	int pcap_ret = 0;

	Py_BEGIN_ALLOW_THREADS

	/* get all devices */
	pcap_ret = pcap_findalldevs(&pcap_list,
								err_buff);
	
	Py_END_ALLOW_THREADS

	if (-1 == pcap_ret)
	{
		PyErr_SetString(PyExc_Exception, err_buff);
		return NULL;
	}

	/* calucate tuple size */
	for (pcap_list_iter = pcap_list; NULL != pcap_list_iter; pcap_list_iter = pcap_list_iter->next)
	{
		++py_dev_tuple_size;
	}

	/* create new tuple for the list */
	py_dev_tuple = PyTuple_New(py_dev_tuple_size);
	if (NULL == py_dev_tuple)
	{
		Py_BEGIN_ALLOW_THREADS

		pcap_freealldevs(pcap_list);

		Py_END_ALLOW_THREADS

		return NULL;
	}

	/* add all items to the tuple */
	for (i = 0, pcap_list_iter = pcap_list; NULL != pcap_list_iter; ++i, pcap_list_iter = pcap_list_iter->next)
	{
		/* create device descriptor tuple */
		PyObject * py_device_desc = PyObject_CallFunction((PyObject*)&pywinpcap_device_type, "(ss)",
									pcap_list_iter->name, pcap_list_iter->description); 

		if (NULL == py_device_desc)
		{
			Py_DECREF(py_dev_tuple);
			pcap_freealldevs(pcap_list);
			return NULL;
		}

		/* set device in list */
		PyTuple_SetItem(py_dev_tuple, i, py_device_desc);
	}

	return py_dev_tuple;
}

PyDoc_STRVAR(find_all_devices_doc,
"find_all_devices() -> Get a list of all available devices");

static PyMethodDef pywinpcap_methods[] = {
	{"get_winpcap_version",	(PyCFunction)py_get_winpcap_version,	METH_VARARGS,	get_winpcap_version_doc},
	{"find_all_devices",	(PyCFunction)py_find_all_devices,	METH_VARARGS,	find_all_devices_doc},
	{NULL,		NULL}		/* sentinel */
};

PyDoc_STRVAR(module_doc,
"Module wrapper around WinPCap");

PyMODINIT_FUNC
initpywinpcap(void)
{
	PyObject * module = NULL;

	if ((PyType_Ready(&pywinpcap_device_type) < 0) ||
		(PyType_Ready(&pywinpcap_capture_type) < 0) ||
		(PyType_Ready(&pywinpcap_packet_type) < 0))
    {
		return;
	}

	module = Py_InitModule3("pywinpcap", pywinpcap_methods, module_doc);

	Py_INCREF(&pywinpcap_capture_type);
    PyModule_AddObject(module, "Capture", (PyObject *)&pywinpcap_capture_type);

	PyExc_PyWinPCapError = PyErr_NewException("pywinpcap.error", NULL, NULL);
	if (NULL == PyExc_PyWinPCapError)
	{
		PyErr_SetString(PyExc_Exception, "Error creating pywinpcap.error exception class");
		return ;
	}

	Py_INCREF(PyExc_PyWinPCapError);
	PyModule_AddObject(module, "error", PyExc_PyWinPCapError);

	PyExc_PyWinPCapTimeoutError = PyErr_NewException("pywinpcap.error.timeout", NULL, NULL);
	if (NULL == PyExc_PyWinPCapTimeoutError)
	{
		PyErr_SetString(PyExc_PyWinPCapError, "Error creating pywinpcap.error.timeout exception class");
		return ;
	}
	
	/* TBD - should I adjust ref-count? */
	if (-1 == PyObject_SetAttrString(PyExc_PyWinPCapError, "timeout", PyExc_PyWinPCapTimeoutError))
	{
		PyErr_SetString(PyExc_PyWinPCapError, "Error setting pywinpcap.error.timeout exception class in pywinpcap.error");
		return ;
	}

	return;
}